package com.atguigu.daijia.order.service.impl;

import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.model.entity.order.*;
import com.atguigu.daijia.model.enums.OrderStatus;
import com.atguigu.daijia.model.form.order.OrderInfoForm;
import com.atguigu.daijia.model.form.order.StartDriveForm;
import com.atguigu.daijia.model.form.order.UpdateOrderBillForm;
import com.atguigu.daijia.model.form.order.UpdateOrderCartForm;
import com.atguigu.daijia.model.vo.base.PageVo;
import com.atguigu.daijia.model.vo.order.*;
import com.atguigu.daijia.order.mapper.OrderBillMapper;
import com.atguigu.daijia.order.mapper.OrderInfoMapper;
import com.atguigu.daijia.order.mapper.OrderProfitsharingMapper;
import com.atguigu.daijia.order.mapper.OrderStatusLogMapper;
import com.atguigu.daijia.order.service.OrderInfoService;
import com.atguigu.daijia.order.service.OrderMonitorService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.x.protobuf.MysqlxCrud;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private OrderStatusLogMapper orderStatusLogMapper;
    @Autowired
    private OrderBillMapper orderBillMapper;
    @Autowired
    private OrderProfitsharingMapper orderProfitsharingMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private OrderMonitorService orderMonitorService;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    //乘客下单后-->创建订单
    @Override
    public Long saveOrderInfo(OrderInfoForm orderInfoForm) {
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderInfoForm,orderInfo);
        //生成订单号
        orderInfo.setOrderNo(UUID.randomUUID().toString().replaceAll("-",""));
        orderInfo.setStatus(1);
        //插入订单信息
        orderInfoMapper.insert(orderInfo);
        //记录订单日志
        this.log(orderInfo.getId(),orderInfo.getStatus());
        //抢单标识：通过key的存在与否判断订单是否已被抢，存入订单ID。
        String key = RedisConstant.ORDER_ACCEPT_MARK + orderInfo.getId();
        redisTemplate.opsForValue().set(key,"0",15, TimeUnit.MINUTES);

        //发送延时消息
        rabbitTemplate.convertAndSend(
                "delay.direct",//交换机
                "delay",//routingkey
                orderInfo.getId(),//消息
                message -> {//设置延时时间
                    message.getMessageProperties().setDelay(120000);
                    return message;
                });

        //返回订单ID
        return orderInfo.getId();
    }

    //根据订单id查询订单的状态
    @Override
    public Integer getOrderStatus(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo==null){
            return OrderStatus.NULL_ORDER.getStatus();
        }
        return orderInfo.getStatus();
    }

    //司机抢单 TODO 使用Redisson实现分布式锁（重点）
    @Override
    public Boolean robNewOrder(Long driverId, Long orderId) {
        //判断订单是否已被抢
        String key = RedisConstant.ORDER_ACCEPT_MARK + orderId;
        if (!redisTemplate.hasKey(key)){
            //抢单失败
            throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
        }
        // 初始化分布式锁，创建一个RLock实例
        RLock lock = redissonClient.getLock("rob:new:order:lock" + orderId);
        try {
            //获取锁
            boolean tryLock = lock.tryLock(1, 1, TimeUnit.SECONDS);
            if (tryLock) {
                //双重判断，因为开始若有两线程查询到可以抢，然后当前线程释放锁后，
                // 其中一个线程立马获得锁，就会导致重复抢单
                if (!redisTemplate.hasKey(key)){
                    //抢单失败
                    throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
                }

                //开始抢单-->修改订单状态为已结单，并修改司机ID，修改接单时间
                LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(OrderInfo::getId,orderId);
                OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
                //设置接单时间
                orderInfo.setAcceptTime(new Date());
                //设置接单的司机ID
                orderInfo.setDriverId(driverId);
                //修改为已接单状态
                orderInfo.setStatus(2);
                int rows = orderInfoMapper.updateById(orderInfo);
                if (rows!=1){
                    //抢单失败
                    throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
                }
                //删除抢单的标识
                redisTemplate.delete(key);
            }
        } catch (Exception e) {
            //抢单失败
            throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
        }finally {
            //释放锁
            if (lock.isLocked()){
                lock.unlock();
            }
        }
        return true;
    }

    //乘客端查找当前订单
    @Override
    public CurrentOrderInfoVo searchCustomerCurrentOrder(Long customerId) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getCustomerId,customerId);
        //乘客端支付完订单，乘客端主要流程就走完（当前这些节点，乘客端会调整到相应的页面处理逻辑）
        Integer[] statusArray = {
                OrderStatus.ACCEPTED.getStatus(),
                OrderStatus.DRIVER_ARRIVED.getStatus(),
                OrderStatus.UPDATE_CART_INFO.getStatus(),
                OrderStatus.START_SERVICE.getStatus(),
                OrderStatus.END_SERVICE.getStatus(),
                OrderStatus.UNPAID.getStatus()
        };
        wrapper.in(OrderInfo::getStatus,statusArray);
        //最新的订单
        wrapper.orderByDesc(OrderInfo::getId);
        wrapper.last("limit 1");

        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
        CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo();
        if (orderInfo!=null){
            currentOrderInfoVo.setOrderId(orderInfo.getId());
            currentOrderInfoVo.setStatus(orderInfo.getStatus());
            currentOrderInfoVo.setIsHasCurrentOrder(true);//表示当前乘客有订单
        }else {
            currentOrderInfoVo.setIsHasCurrentOrder(false);//表示当前乘客没有订单
        }
        return currentOrderInfoVo;
    }

    //司机端查找当前的订单
    @Override
    public CurrentOrderInfoVo searchDriverCurrentOrder(Long driverId) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getDriverId, driverId);
        Integer[] statusArray = {
                OrderStatus.ACCEPTED.getStatus(),
                OrderStatus.DRIVER_ARRIVED.getStatus(),
                OrderStatus.UPDATE_CART_INFO.getStatus(),
                OrderStatus.START_SERVICE.getStatus(),
                OrderStatus.END_SERVICE.getStatus(),
                OrderStatus.UNPAID.getStatus()
        };
        wrapper.in(OrderInfo::getStatus,statusArray);
        //最新的订单
        wrapper.orderByDesc(OrderInfo::getId);
        wrapper.last("limit 1");

        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
        CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo();
        if (orderInfo!=null){
            currentOrderInfoVo.setOrderId(orderInfo.getId());
            currentOrderInfoVo.setStatus(orderInfo.getStatus());
            currentOrderInfoVo.setIsHasCurrentOrder(true);//表示当前司机有订单
        }else {
            currentOrderInfoVo.setIsHasCurrentOrder(false);//表示当前司机没有订单
        }
        return currentOrderInfoVo;
    }

    //司机到达起始点,更新订单信息
    @Override
    public Boolean driverArriveStartLocation(Long orderId, Long driverId) {
        //条件
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getId, orderId);
        wrapper.eq(OrderInfo::getDriverId, driverId);
        OrderInfo orderInfo = new OrderInfo();
        //更新订单状态
        orderInfo.setStatus(3);
        //更新到达时间
        orderInfo.setArriveTime(new Date());
        int update = orderInfoMapper.update(orderInfo, wrapper);
        if (update<=0){
            throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
        }
        //记录日志
        this.log(orderId,3);
        return true;
    }

    //司机到达起始点,更新代驾车辆信息(车前后照片、车牌号等)
    @Override
    public Boolean updateOrderCart(UpdateOrderCartForm updateOrderCartForm) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getId, updateOrderCartForm.getOrderId());
        wrapper.eq(OrderInfo::getDriverId, updateOrderCartForm.getDriverId());

        //要更新的数据，包括车前后照片、车牌照
        OrderInfo updateOrderInfo = new OrderInfo();
        BeanUtils.copyProperties(updateOrderCartForm, updateOrderInfo);
        updateOrderInfo.setStatus(4);
        //只能更新自己的订单
        int row = orderInfoMapper.update(updateOrderInfo, wrapper);
        if (row<=0){
            throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
        }
        //记录日志
        this.log(updateOrderInfo.getId(),4);
        return true;
    }

    //司机开始代驾服务，更新订单状态
    @Override
    public Boolean startDrive(StartDriveForm startDriveForm) {
        //条件
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getId, startDriveForm.getOrderId());
        wrapper.eq(OrderInfo::getDriverId, startDriveForm.getDriverId());
        //修改的数据
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setStatus(5);
        orderInfo.setStartServiceTime(new Date());

        int rows = orderInfoMapper.update(orderInfo, wrapper);
        if (rows<=0){
            throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
        }
        //记录日志
        this.log(startDriveForm.getOrderId(), 5);

        //初始化订单监控统计数据
        OrderMonitor orderMonitor = new OrderMonitor();
        orderMonitor.setOrderId(startDriveForm.getOrderId());
        orderMonitorService.saveOrderMonitor(orderMonitor);
        return true;
    }

    //根据时间段获取订单数
    @Override
    public Long getOrderNumByTime(String startTime, String endTime) {
        //示例：9<=time<10   10<=time<11
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(OrderInfo::getStartServiceTime, startTime);
        wrapper.lt(OrderInfo::getStartServiceTime, endTime);
        Long count = orderInfoMapper.selectCount(wrapper);
        return count;
    }

    //结束代驾服务更新订单账单
    @Override
    public Boolean endDrive(UpdateOrderBillForm updateOrderBillForm) {
        //更新订单表(包括)
        //条件
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getId, updateOrderBillForm.getOrderId());
        wrapper.eq(OrderInfo::getDriverId, updateOrderBillForm.getDriverId());
        //修改的数据
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setStatus(6);//结束服务
        orderInfo.setRealAmount(updateOrderBillForm.getTotalAmount());//实际金额
        orderInfo.setRealDistance(updateOrderBillForm.getRealDistance());//实际里程
        orderInfo.setEndServiceTime(new Date());//结束服务时间
        orderInfo.setFavourFee(updateOrderBillForm.getFavourFee());//小费

        int rows = orderInfoMapper.update(orderInfo, wrapper);
        if (rows<=0){
            throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
        }
        //添加账单数据
        OrderBill orderBill = new OrderBill();
        BeanUtils.copyProperties(updateOrderBillForm, orderBill);
        orderBill.setOrderId(updateOrderBillForm.getOrderId());
        orderBill.setPayAmount(updateOrderBillForm.getTotalAmount());
        orderBillMapper.insert(orderBill);

        //插入分账信息数据
        OrderProfitsharing orderProfitsharing = new OrderProfitsharing();
        BeanUtils.copyProperties(updateOrderBillForm, orderProfitsharing);
        orderProfitsharing.setOrderId(updateOrderBillForm.getOrderId());
        orderProfitsharing.setRuleId(updateOrderBillForm.getProfitsharingRuleId());
        orderProfitsharing.setStatus(1);
        orderProfitsharingMapper.insert(orderProfitsharing);

        //记录日志
        this.log(updateOrderBillForm.getOrderId(), 6);
        return true;
    }

    //获取乘客订单分页列表
    @Override
    public PageVo findCustomerOrderPage(Page<OrderInfo> pageParam, Long customerId) {
        IPage<OrderListVo> page=orderInfoMapper.selectOrderByPage(pageParam, customerId);

        PageVo<OrderListVo> pageVo = new PageVo<>();
        pageVo.setRecords(page.getRecords());
        pageVo.setPages(page.getPages());
        pageVo.setTotal(page.getTotal());
        pageVo.setPage(page.getCurrent());
        pageVo.setLimit(page.getSize());
        return pageVo;
    }

    //获取司机订单分页列表
    @Override
    public PageVo findDriverOrderPage(Page<OrderInfo> pageParam, Long driverId) {
        IPage<OrderListVo> page = orderInfoMapper.selectDriverOrderPage(pageParam, driverId);
        PageVo<OrderListVo> pageVo = new PageVo<>();
        pageVo.setLimit(page.getSize());//页大小
        pageVo.setPage(page.getCurrent());//当期页
        pageVo.setPages(page.getPages());//总页数
        pageVo.setRecords(page.getRecords());//记录
        return pageVo;
    }

    //根据订单id获取实际账单信息
    @Override
    public OrderBillVo getOrderBillInfo(Long orderId) {
        LambdaQueryWrapper<OrderBill> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderBill::getOrderId, orderId);
        OrderBill orderBill = orderBillMapper.selectOne(wrapper);

        OrderBillVo orderBillVo = new OrderBillVo();
        BeanUtils.copyProperties(orderBill,orderBillVo);
        return orderBillVo;
    }

    //根据订单id获取实际分账信息
    @Override
    public OrderProfitsharingVo getOrderProfitsharing(Long orderId) {
        LambdaQueryWrapper<OrderProfitsharing> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderProfitsharing::getOrderId, orderId);
        OrderProfitsharing orderProfitsharing = orderProfitsharingMapper.selectOne(wrapper);

        OrderProfitsharingVo orderProfitsharingVo = new OrderProfitsharingVo();
        BeanUtils.copyProperties(orderProfitsharing,orderProfitsharingVo);
        return orderProfitsharingVo;
    }

    //司机发送账单信息,实际就是更新订单的状态
    @Override
    public Boolean sendOrderBillInfo(Long orderId, Long driverId) {
        //条件
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getId, orderId);
        wrapper.eq(OrderInfo::getDriverId,driverId);

        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setStatus(7);//待付款
        int rows = orderInfoMapper.update(orderInfo, wrapper);
        if (rows<=0){
            throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
        }
        //记录日志-->待付款状态
        this.log(orderId,7);
        return true;
    }

    //获取订单支付信息
    @Override
    public OrderPayVo getOrderPayVo(String orderNo, Long customerId) {
        OrderPayVo orderPayVo=orderInfoMapper.selectOrderPayVo(orderNo, customerId);
        if (orderPayVo!=null){
            String content = orderPayVo.getStartLocation() + " 到 " + orderPayVo.getEndLocation();
            orderPayVo.setContent(content);
        }
        return orderPayVo;
    }


    //记录订单日志
    public void log(Long orderId, Integer status) {
        OrderStatusLog orderStatusLog = new OrderStatusLog();
        orderStatusLog.setOrderId(orderId);//订单ID
        orderStatusLog.setOrderStatus(status);//状态
        orderStatusLog.setOperateTime(new Date());//操作时间
        orderStatusLogMapper.insert(orderStatusLog);
    }

    //布隆过滤器的实现
    public void bloomFilter(){
        //构造布隆过滤器
        RBloomFilter<Integer> bloomFilter1 = redissonClient.getBloomFilter("bloomFilter1");
        bloomFilter1.tryInit(1000, 0.05);//设置误判率
        bloomFilter1.add(50);
        bloomFilter1.contains(40);//false
    }

}
